﻿/**
 * 
 * Copyright 2009 Mike Jamer [email: mikejamer@gmail.com ]
 * 
 * This File is part of Moose IRC
 * 
 * MooseIRC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MooseIRC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MooseIRC.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

using moose.utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace moose.irc
{
    /// <summary>
    /// Primary Point of reference, lets call this the client
    /// </summary>
    [CLSCompliant(true)]
    public sealed class IrcClient : object
    {
        /// <summary>
        /// Initialize Irc Client
        /// </summary>
        public IrcClient()
            : base()
        {
            _connection = new IrcConnection();
            _connection.Response += onServerMessage;

            StatusMessage += onStatusMessage;

            JoinChannel += onJoinChannel;
            PartChannel += onPartChannel;

            _dictChannels = new Dictionary<string, IrcChannel>();
            _dictServerSettings = new Dictionary<string, string>();

            return;
        }

        /// <summary>
        /// Current Irc Session State
        /// </summary>
        private IrcState _current_state;

        /// <summary>
        /// Connection to IRC Server
        /// </summary>
        private IrcConnection _connection;

        /// <summary>
        /// Signals a status message, a message that will likely go to the status window
        /// </summary>
        public event ResponseHandler StatusMessage;

        #region [Server Settings]

        /// <summary>
        /// Collects Various Server Settings
        /// </summary>
        private Dictionary<string, string> _dictServerSettings;
        
        /// <summary>
        /// Gets Server Setting
        /// </summary>
        /// <param name="key">Setting Key</param>
        /// <returns>Server Setting</returns>
        public string getServerSetting(string key)
        {
            return _dictServerSettings[key];
        }

        /// <summary>
        /// Sets Server Setting
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void setServerSetting(string key, string value)
        {
            _dictServerSettings[key] = value;
            return;
        }

        #endregion

        #region [Channels]

        /// <summary>
        /// Channels we're member of
        /// </summary>
        private Dictionary<string, IrcChannel> _dictChannels;

        /// <summary>
        /// Event Indicating a joined channel
        /// </summary>
        public event ChannelHandler JoinChannel;

        /// <summary>
        /// Event Indicating a parted channel
        /// </summary>
        public event ChannelHandler PartChannel;

        /// <summary>
        /// Enumerate All Channels
        /// </summary>
        /// <returns>Colleciton of Channels</returns>
        public IEnumerable<IrcChannel> getChannels()
        {
            return _dictChannels.Values;
        }

        /// <summary>
        /// Gets a Channel
        /// </summary>
        /// <param name="channel_name"></param>
        /// <returns></returns>
        public IrcChannel getChannel(string channel_name)
        {
            return _dictChannels[channel_name];
        }

        /// <summary>
        /// Join a channel
        /// </summary>
        /// <param name="channel_name">name of channel to join</param>
        /// <returns>instance of channel we just joined</returns>
        public IrcChannel joinChannel(string channel_name)
        {
            if (_dictChannels.ContainsKey(channel_name))
            {
                return _dictChannels[channel_name];
            }
            else
            {
                IrcChannel chan = new IrcChannel() { Name = channel_name };
                _dictChannels[channel_name] = chan;
                //JoinChannel(chan);
                return chan;
            }
        }

        public void NotifyChannel(string channel_name) {
            IrcChannel chan = _dictChannels[channel_name];
            JoinChannel(chan);
            return;
        }

        /// <summary>
        /// Parts channel
        /// </summary>
        /// <param name="channel_name">Name of channel to part</param>
        public void partChannel(string channel_name)
        {
            if (_dictChannels.ContainsKey(channel_name))
            {
                PartChannel(_dictChannels[channel_name]);
                _dictChannels.Remove(channel_name);
                
            }
        }

        /// <summary>
        /// Internal Join Channel Handler
        /// </summary>
        /// <param name="channel">Channel Joined</param>
        private void onJoinChannel(IrcChannel channel) {
            return;
        }

        /// <summary>
        /// Internal Part Channel Handler
        /// </summary>
        /// <param name="channel">Channel parted0</param>
        private void onPartChannel(IrcChannel channel) {
            return;
        }

        #endregion

        /// <summary>
        /// Gets/Sets Current IrcState
        /// </summary>
        public IrcState SessionState
        {
            get
            {
                return _current_state;
            }
            set
            {
                _current_state = value;

                return;
            }
        }

        /// <summary>
        /// Just Echo a message to the status window
        /// </summary>
        /// <param name="message">message to echo</param>
        public void echo(string message)
        {
            StatusMessage(message);
            return;
        }

        /// <summary>
        /// Attempt to connect to the server
        /// </summary>
        /// <param name="host">Host address to connect to</param>
        /// <param name="port">Host Port to connect to</param>
        /// <returns>IRC Session State, Connected is what you should see initially
        /// </returns>
        public IrcState connect(string host, int port)
        {

            _connection.connect(host, port);
            if (_connection.Connected)
            {
                SessionState = IrcState.Connected;
            }
            else
            {
                SessionState = IrcState.Disconnected;
            }

            return SessionState;
        }

        /// <summary>
        /// Attempt to disconnect from the IRC Server
        /// </summary>
        /// <returns>IRC Session State, You should see Disconnected</returns>
        public IrcState disconnect()
        {
            if (_connection.disconnect())
            {
                SessionState = IrcState.Disconnected;
            }
            else
            {
                SessionState = IrcState.Connected;
            }
            return SessionState;
        }

        /// <summary>
        /// Internal Status Message
        /// </summary>
        /// <param name="message"></param>
        private void onStatusMessage(string message)
        {
            return;
        }

        /// <summary>
        /// Fires when the server sends data to us
        /// </summary>
        /// <param name="message">message to parse</param>
        private void onServerMessage(string message)
        {
            //Break server responses down into individual messages
            foreach (string line in message.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
            {
                dispatch(new IrcMessage(message));
            }
            return;
        }

        private void dispatch(IrcMessage message)
        {
            if (message.Command == null)
                return;
            IrcAction action = null;
            action = DriverUtility.getResponseAction(message.Command);
            if (action != null)
            { //TODO: Update IrcAction, to now take the managed IrcMessage object, rather than an array
                action.execute(this, message);
            }
            else
            {
                StatusMessage(concatInput(message));
            }

        }

        /// <summary>
        /// Send Command to server
        /// </summary>
        /// <param name="input">input command</param>
        public void send(string input)
        {
            if (input.Length > 1 && input[0] == '/')
                input = input.Substring(1);
            else
                return;

            IrcMessage message = new IrcMessage(input);
            IrcAction action = DriverUtility.getCommandAction(message.Command);

            if (action != null)
            {
                action.execute(this, message);
                return;
            }

            if (SessionState == IrcState.Registered //If we are registered
                || SessionState == IrcState.Connected) //Or connected
            {
                _connection.send(input + "\r\n"); //Send the input as it was recieved
            }

            return;
        }

        /// <summary>
        /// Concatinates input properly
        /// </summary>
        /// <param name="input">array of parameters</param>
        /// <returns>all input</returns>
        private string concatInput(IrcMessage message)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(message.Command); sb.Append(' ');
            foreach (string arg in message.Arguments)
            {
                sb.Append(arg); sb.Append(' ');
            }
            return sb.ToString().Trim();
        }
    }
}
